//
// Created by LONG on 2020/10/18.
//


#include <stdio.h>
#include <iostream>
#include "../lib/doctest.h"
#include "../../include/utils/hashTable.h"
#include "../../include/utils/hashFunc.h"

using namespace std;

TEST_SUITE ("HASH_TABLE_TS")
{
    TEST_CASE ("HASH_TABLE_TC")
    {
        uint64_t timeStamp;
        uint64_t lastTick;
        SUBCASE("HASH_TABLE_SC_BASE1")
        {
#define HASH_TABLE_SIZE 1024
            int32_t rc;
            int32_t key;
            int32_t key1;
            int32_t data;
            int32_t *pData;
            S_HashTable *hash_table;
            S_HashNodeKeyVal *key_value;
            S_HashTableIter iter;

            hash_table = HashTable_Create(HASH_TABLE_SIZE, Hash_Uint32, Equal_Uint32);

            key = rand();
            data = rand();

            rc = HashTable_Insert(hash_table, &key, &data);
            REQUIRE(CSP_RC_OK == rc);

            key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key);
            pData = (int32_t *)(key_value ? key_value->data : NULL);
            REQUIRE(pData);
            REQUIRE(data == *pData);

            key1 = key + 1;
            key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key1);
            pData = (int32_t *)(key_value ? key_value->data : NULL);
            REQUIRE_FALSE(pData);

            rc = HashTable_Capacity(hash_table);
            REQUIRE(HASH_TABLE_SIZE == rc);

            rc = HashTable_Count(hash_table);
            REQUIRE(1 == rc);

            rc = HashTable_Resize(hash_table, HASH_TABLE_SIZE * 2);
            REQUIRE(CSP_RC_OK == rc);

            rc = HashTable_Capacity(hash_table);
            REQUIRE(HASH_TABLE_SIZE * 2 == rc);

            rc = HashTable_Count(hash_table);
            REQUIRE(1 == rc);

            HashTable_Iterate(hash_table, &iter);

            rc = HashTableIter_HasMore(&iter);
            REQUIRE(rc);

            key_value = HashTableIter_Next(&iter);
            REQUIRE(key_value);
            REQUIRE(key == *(int32_t *)key_value->key);
            REQUIRE(data == *(int32_t *)key_value->data);

            rc = HashTableIter_HasMore(&iter);
            REQUIRE_FALSE(rc);

            key_value = HashTableIter_Next(&iter);
            REQUIRE_FALSE(key_value);

            REQUIRE(HashTable_Remove(hash_table, &key));

            rc = HashTable_Count(hash_table);
            REQUIRE(0 == rc);

            HashTable_Destroy(hash_table);
        }

        SUBCASE("HASH_TABLE_SC_BASE2")
        {
            int32_t rc;
            int32_t key;
            int32_t key1;
            int32_t key2;
            int32_t data;
            S_HashTable *hash_table;

            hash_table = HashTable_Create(1, Hash_Uint32, Equal_Uint32);

            key = rand();
            data = rand();
            key1 = key + 1;
            key2 = key1 + 1;

            rc = HashTable_Insert(hash_table, &key, &data);
            REQUIRE(CSP_RC_OK == rc);

            rc = HashTable_Insert(hash_table, &key1, &data);
            REQUIRE(CSP_RC_ER == rc);

            rc = HashTable_Resize(hash_table, 2);
            REQUIRE(CSP_RC_OK == rc);

            rc = HashTable_Insert(hash_table, &key1, &data);
            REQUIRE(CSP_RC_OK == rc);

            rc = HashTable_Insert(hash_table, &key2, &data);
            REQUIRE(CSP_RC_ER == rc);

            HashTable_Destroy(hash_table);
        }

        SUBCASE("HASH_TABLE_SC_hash_conflict1")
        {
#define HASH_TABLE_SIZE 1024
            int32_t rc;
            int32_t key;
            int32_t key1;
            int32_t data;
            int32_t data1;
            int32_t *pData;
            S_HashNodeKeyVal *key_value;
            S_HashTable *hash_table;

            hash_table = HashTable_Create(HASH_TABLE_SIZE, Hash_Uint32, Equal_Uint32);

            key = rand();
            data = rand();
            key1 = key + HASH_TABLE_SIZE;
            data1 = rand();

            rc = HashTable_Insert(hash_table, &key, &data);
            REQUIRE(CSP_RC_OK == rc);
            rc = HashTable_Insert(hash_table, &key1, &data1);
            REQUIRE(CSP_RC_OK == rc);

            key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key);
            pData = (int32_t *)(key_value ? key_value->data : NULL);
            REQUIRE(pData);
            REQUIRE(data == *pData);

            key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key1);
            pData = (int32_t *)(key_value ? key_value->data : NULL);
            REQUIRE(pData);
            REQUIRE(data1 == *pData);

            rc = HashTable_Count(hash_table);
            REQUIRE(2 == rc);

            REQUIRE(HashTable_Remove(hash_table, &key));
            REQUIRE(HashTable_Remove(hash_table, &key1));

            rc = HashTable_Count(hash_table);
            REQUIRE(0 == rc);

            HashTable_Destroy(hash_table);
        }

        SUBCASE("HASH_TABLE_SC_hash_conflict2")
        {
#define HASH_TABLE_SIZE 1024
            int32_t rc;
            uint32_t loop;
            int32_t key[HASH_TABLE_SIZE];
            int32_t key1[HASH_TABLE_SIZE];
            int32_t data[HASH_TABLE_SIZE];
            int32_t data1[HASH_TABLE_SIZE];
            int32_t *pData;
            S_HashNodeKeyVal *key_value;
            S_HashTable *hash_table;

            hash_table = HashTable_Create(HASH_TABLE_SIZE * 2, Hash_Uint32, Equal_Uint32);

            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                key[loop] = rand();
                key1[loop] = key[loop] + HASH_TABLE_SIZE;
                data[loop] = rand();
                data1[loop] = rand();
            }
            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                rc = HashTable_Insert(hash_table, &key[loop], &data[loop]);
                REQUIRE(CSP_RC_OK == rc);
                rc = HashTable_Insert(hash_table, &key1[loop], &data1[loop]);
                REQUIRE(CSP_RC_OK == rc);
            }

            rc = HashTable_Count(hash_table);
            REQUIRE(HASH_TABLE_SIZE * 2 == rc);

            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key[loop]);
                pData = (int32_t *)(key_value ? key_value->data : NULL);
                REQUIRE(pData);
                REQUIRE(data[loop] == *pData);

                key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key1[loop]);
                pData = (int32_t *)(key_value ? key_value->data : NULL);
                REQUIRE(pData);
                REQUIRE(data1[loop] == *pData);
            }

            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                REQUIRE(HashTable_Remove(hash_table, &key[loop]));
                REQUIRE(HashTable_Remove(hash_table, &key1[loop]));
            }

            rc = HashTable_Count(hash_table);
            REQUIRE(0 == rc);

            HashTable_Destroy(hash_table);
        }

        SUBCASE("HASH_TABLE_SC_hash_conflict3")
        {
#define HASH_TABLE_SIZE 1024
            int32_t rc;
            uint32_t loop;
            int32_t key[HASH_TABLE_SIZE];
            int32_t key1[HASH_TABLE_SIZE];
            int32_t data[HASH_TABLE_SIZE];
            int32_t data1[HASH_TABLE_SIZE];
            int32_t *pData;
            S_HashNodeKeyVal *key_value;
            S_HashTable *hash_table;

            hash_table = HashTable_Create(HASH_TABLE_SIZE, Hash_Uint32, Equal_Uint32);

            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                key[loop] = rand();
                key1[loop] = key[loop];
                data[loop] = rand();
                data1[loop] = rand();
            }
            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                rc = HashTable_Insert(hash_table, &key[loop], &data[loop]);
                REQUIRE(CSP_RC_OK == rc);
                rc = HashTable_Insert(hash_table, &key1[loop], &data1[loop]);
                REQUIRE(CSP_RC_OK == rc);
            }

            rc = HashTable_Count(hash_table);
            REQUIRE(HASH_TABLE_SIZE == rc);

            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key[loop]);
                pData = (int32_t *)(key_value ? key_value->data : NULL);
                REQUIRE(pData);
                REQUIRE(data1[loop] == *pData);

                key_value = (S_HashNodeKeyVal *)HashTable_Lookup(hash_table, &key1[loop]);
                pData = (int32_t *)(key_value ? key_value->data : NULL);
                REQUIRE(pData);
                REQUIRE(data1[loop] == *pData);
            }

            for (loop = 0; loop < HASH_TABLE_SIZE; loop++)
            {
                REQUIRE(HashTable_Remove(hash_table, &key[loop]));
                REQUIRE(!HashTable_Remove(hash_table, &key1[loop]));
            }

            rc = HashTable_Count(hash_table);
            REQUIRE(0 == rc);

            HashTable_Destroy(hash_table);
        }

        SUBCASE("HASH_TABLE_SC_hash_random_data")
        {
#define HASH_TABLE_SIZE 1024
            uint32_t loop;
            int32_t key;
            int32_t data;
            S_HashTable *hash_table;

            hash_table = HashTable_Create(HASH_TABLE_SIZE, Hash_Uint32, Equal_Uint32);

            for (loop = 0; loop < 100000; loop++)
            {
                key = rand();
                data = rand();
                HashTable_Insert(hash_table, &key, &data);

                key = rand();
                HashTable_Lookup(hash_table, &key);
                HashTable_Remove(hash_table, &key);

                HashTable_Resize(hash_table, rand() % HASH_TABLE_SIZE);
                HashTable_Capacity(hash_table);
                HashTable_Count(hash_table);
            }

            HashTable_Destroy(hash_table);

            REQUIRE(OS_MemGetAllocCount() == OS_MemGetFreeCount());

            OS_MemResetCounter();
        }

        SUBCASE("HASH_TABLE_SC_MISC")
        {
            uint32_t value1;
            uint32_t value2;
            const char *str1 = "aaaa";
            const char *str2 = "aaaa";
            const char *str3 = "bbbb";

            value1 = rand();
            value2 = value1;
            REQUIRE(value1 == Hash_Uint32(&value1));
            REQUIRE(Hash_Uint32(&value2) == Hash_Uint32(&value1));
            REQUIRE(Equal_Uint32(&value1, &value2));
            value2++;
            REQUIRE(Hash_Uint32(&value2) != Hash_Uint32(&value1));
            REQUIRE_FALSE(Equal_Uint32(&value1, &value2));

            REQUIRE(Hash_String((void *)str1) == Hash_String((void *)str2));
            REQUIRE(Equal_String((void *)str1, (void *)str2));
            REQUIRE(Hash_String((void *)str1) != Hash_String((void *)str3));
            REQUIRE_FALSE(Equal_String((void *)str1, (void *)str3));
        }
    }
}
